
#include <BClass>
#include <BKeyEvent>
#include <BApplication>

#include "SourceFormat.h"
#include "PromptListFilter.h"

PromptListFilter::PromptListFilter()
{
	_index = -1;
	_listWidget = new BListWidget();
	_listWidget->setHorizontalScrollHint(ScrollHint_None);
	_listWidget->hide();
	connect(_listWidget, Signal_ItemClicked, &PromptListFilter::slotListItemClicked);
	connect(_listWidget, Signal_AreaChanged, &PromptListFilter::slotListAreaResized);
}
PromptListFilter::~PromptListFilter()
{

}

void PromptListFilter::prompt(const SourceFormat::Item* item, int index)
{
	_index = -1;
	_listWidget->clearItems();
	if (item->exp)
	{
		_index = index;
		if (item->exp->syntax == Syntax_Name)
		{
			BString name;
			const BStyle* style = _listWidget->realStyle();
			const BClass* cls = item->exp->value;
			while (cls)
			{
				for (int i = 0; i < cls->methodCount(); i++)
				{
					cls->method(i, name);
					name.truncate(0, name.find('('));
					BListItem* item = _listWidget->addItem(name);
					item->setIcon(style->icon("method"));
				}
				for (int i = 0; i < cls->variantCount(); i++)
				{
					cls->variant(i, name);
					BListItem* item = _listWidget->addItem(name);
					item->setIcon(style->icon("variant"));
				}
				cls = cls->super();
			}
		}
	}
	if (_index >= 0)
	{
		_listWidget->sortItems();
		_listWidget->show();
	}
}
void PromptListFilter::finish()
{
	if (_listWidget->visible())
	{
		_listWidget->hide();
		_head.clear();
		_index = -1;
		_cueItem = 0;
	}
}
bool PromptListFilter::checkCueItem(const BListItem* item)
{
	if (_head.empty())
		return true;
	BString text = item->text();
	text.lower();
	return text.beginWith(_head);
}

void PromptListFilter::slotTextEdited(BObject* object, const BValue& value)
{
	if (_index >= 0)
	{
		_head.reset();
		int pos = _textEdit->inputPos();
		for (int i = _index; i < pos; i++)
		{
			BCode code = _textEdit->code(i);
			_head.append(code);
		}
		if (pos < _index)
		{
			_index = -1;
			_listWidget->hide();
			return;
		}
		for (int i = 0; i < _listWidget->itemCount(); i++)
		{
			BListItem* item = _listWidget->item(i);
			item->setVisible(checkCueItem(item));
		}
		for (int i = 0; i < _listWidget->itemCount(); i++)
		{
			BListItem* item = _listWidget->item(i);
			if (item->visible())
			{
				item->setChecked(true);
				_cueItem = item;
				break;
			}
		}
	}

}
void PromptListFilter::slotListItemClicked(BObject* object, const BValue& value)
{
	BListItem* item = value;
	_cueItem = item;
	if (_cueItem)
	{
		int pos = _textEdit->inputPos();
		_textEdit->select(_index, pos);
		_textEdit->inputText(_cueItem->text());
		_textEdit->setFocused(true);
		_listWidget->hide();
	}
}
void PromptListFilter::slotListAreaResized(BObject* object, const BValue& value)
{
	const BRect& areaSize = value;
	BSize csize = _listWidget->centerSize();
	if (csize.width() < areaSize.width())
		csize.width() = areaSize.width();
	BSize prefSize = _listWidget->preferSize(csize);
	_listWidget->setSize(prefSize);
}
void PromptListFilter::slotSourceEditFreshed(BObject* object, const BValue& value)
{
	if (_listWidget->visible())
	{
		BRect codeRect = _textEdit->codeRect(_index - 1);
		_listWidget->setPos(codeRect.rightBottom());
	}
}

void PromptListFilter::attached(BWidget* widget)
{
	_listWidget->setParent(widget);
	_textEdit = dynamic_cast<BTextEdit*>(widget);
	connect(widget, Signal_TextEdited, &PromptListFilter::slotTextEdited);
	connect(widget, Signal_Freshed, &PromptListFilter::slotSourceEditFreshed);
}
void PromptListFilter::detached(BWidget* widget)
{
	_listWidget->setParent(0);
	disconnect(widget);
}

void PromptListFilter::focusEvent(const BEvent& event)
{
	finish();
	BEventFilter::focusEvent(event);
}
void PromptListFilter::inputEvent(const BEvent& event)
{
	BSource* source = _textEdit->asset();
	SourceFormat* format = (SourceFormat*)_textEdit->textFormat();
	BCode code = event.value();
	if (code == '.')
	{
		int pos = _textEdit->inputPos();
		const SourceFormat::Item* item = format->item(pos - 1);
		prompt(item, pos + 1);
	}
	else if (code == '>')
	{
		int pos = _textEdit->inputPos();
		BCode prev = _textEdit->code(pos - 1);
		if (prev == '-')
		{
			const SourceFormat::Item* item = format->item(pos - 2);
			prompt(item, pos + 1);
		}
	}
	else if (code.isPunct())
	{
		_listWidget->hide();
	}
	BEventFilter::inputEvent(event);
}

void PromptListFilter::keyPressEvent(const BKeyEvent& keyEvent)
{
	if (_listWidget->visible())
	{
		Key key = keyEvent.key();
		if (key == Key_Tab)
		{
			if (_cueItem)
			{
				int pos = _textEdit->inputPos();
				_textEdit->select(_index, pos);
				_textEdit->inputText(_cueItem->text());
				finish();
				keyEvent.accept();
			}
		}
		else if (key == Key_Escape)
		{
			finish();
		}
		if (_cueItem)
		{
			if (key == Key_Up)
			{
				BListItem* prev = _cueItem->prev();
				while (prev && !prev->visible())
				{
					prev = prev->prev();
				}
				if (prev && prev->visible())
				{
					_cueItem->setChecked(false);
					_cueItem = prev;
					_cueItem->setChecked(true);
				}
				keyEvent.accept();
			}
			else if (key == Key_Down)
			{
				BListItem* next = _cueItem->next();
				while (next && !next->visible())
				{
					next = next->next();
				}
				if (next && next->visible())
				{
					_cueItem->setChecked(false);
					_cueItem = next;
					_cueItem->setChecked(true);
				}
				keyEvent.accept();
			}
			const BRect rect = _cueItem->rect();
			if (!_listWidget->centerRect().contain(rect))
			{
				_listWidget->scrollToItem(_cueItem);
			}
		}
	}
	BEventFilter::keyPressEvent(keyEvent);
}
